home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 July: Mac OS SDK / Dev.CD Jul 97 SDK2.toast / Development Kits (Disc 2) / QuickTime / Sample Code / QT Codec Acceleration / Samples / FakeRaw / rawFakerCodec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-26  |  16.6 KB  |  581 lines  |  [TEXT/MPS ]

  1. #include <Errors.h>
  2. #include <FixMath.h>
  3. #include <LowMem.h>
  4. #include <Memory.h>
  5. #include <OSUtils.h>
  6. #include <Quickdraw.h>    
  7.  
  8. #include "ImageCodec.h"
  9.  
  10. #ifdef    POWERPC_NATIVE
  11.     #pragma    options align=mac68k
  12. #endif
  13.  
  14. /* Version information */
  15.  
  16. #define    EXAMPLE_CODEC_REV            2
  17. #define    codecInterfaceVersion        2                /* high word returned in component GetVersion */
  18.  
  19. /*
  20.     Our data structure declarations
  21. */
  22.  
  23.  
  24. #define QUEUE_SIZE 30
  25.  
  26. #ifndef fieldOffset
  27.     #define fieldOffset(type, field) ((short) &((type *) 0)->field)
  28. #endif
  29.  
  30. /*
  31.     The DecompressRecord is used to store the information needed
  32.     to decompress a frame asynchronously.
  33. */
  34. struct DecompressRecord {
  35.     void *next;                // next DecompressRecord that is queued up
  36.     void *nextFree;            // next unused DecompressRecord
  37.     ICMCompletionProcRecord completionProc;    // completion proc record to call when done
  38.     TimeValue frameTime;    // what time to decompress this frame
  39.     Fixed rate;                // rate of movie
  40.     long scale;                // time scale
  41.     struct Globals *glob;    // pointer to our globals
  42.     Boolean inUse;            // set while this entry is being used
  43. };
  44. typedef struct DecompressRecord DecompressRecord;
  45.  
  46. /* This is the structure we use to store our global data for each instance */
  47.  
  48. typedef struct    {                        
  49.     QTCallBack            callBack;                // our call back
  50.     QTCallBackUPP        decompressCallBackUPP;    // pointer to our decompress callback
  51.     ImageSequence        sequenceID;
  52.     QHdr                head;                    // queue header
  53.     DecompressRecord    *firstFreeEntry;        // first unused entry
  54.     DecompressRecord    queue[QUEUE_SIZE];        // for queued frames
  55.     long                a5World;                // A5 world for decompress callback
  56.     Boolean                queueIsRunning;            // set if decompress queue is currently running
  57. } Globals;
  58.  
  59.  
  60. /* Function prototypes to keep the compiler smiling. */
  61.  
  62. pascal ComponentResult
  63. RAWFAKEREXAMPLECODEC(ComponentParameters *params,char **storage);
  64.  
  65. pascal ComponentResult
  66. CDOpen(ComponentInstance self);
  67.  
  68. pascal ComponentResult
  69. CDClose(Handle storage,ComponentInstance self);
  70.  
  71. pascal ComponentResult
  72. CanDoSelector(short selector);
  73.  
  74. pascal ComponentResult 
  75. GetVersion(void);
  76.  
  77. pascal void
  78. DecompressCallBack(QTCallBack cb,long refcon);
  79.  
  80. pascal long
  81. CDCodecFlush(Handle storage);
  82.  
  83. #ifdef POWERPC_NATIVE
  84.  
  85. /*
  86.     create a RoutineDescriptor for the Component dispatch routine
  87. */
  88. RoutineDescriptor Dispatcher = BUILD_ROUTINE_DESCRIPTOR(kPascalStackBased |
  89.                                                                 RESULT_SIZE(kFourByteCode) |
  90.                                                                 STACK_ROUTINE_PARAMETER(1,kFourByteCode) |
  91.                                                                 STACK_ROUTINE_PARAMETER(2,kFourByteCode),
  92.                                                                 RAWFAKEREXAMPLECODEC);
  93.  
  94. RoutineDescriptor decompressCallBackUPP = BUILD_ROUTINE_DESCRIPTOR(uppQTCallBackProcInfo, DecompressCallBack);
  95. #endif
  96.  
  97. /************************************************************************************ 
  98.  *    This is the main dispatcher for our codec. All calls from the codec manager
  99.  *    will come through here, with a unique selector and corresponding parameter block.
  100.  *
  101.  *    This routine must be first in the code segment of the codec thing.
  102.  */
  103.  
  104. pascal ComponentResult
  105. RAWFAKEREXAMPLECODEC(ComponentParameters *params,char **storage)
  106. {    
  107.     switch ( params->what ) {
  108.  
  109.         case kComponentOpenSelect:
  110.             {
  111.                 ComponentCallOpenParameters *p = (ComponentCallOpenParameters *)params;
  112.                 return CDOpen(p->self);
  113.             }
  114.  
  115.         case kComponentCloseSelect:
  116.             {
  117.                 ComponentCallCloseParameters *p = (ComponentCallCloseParameters *)params;
  118.                 return CDClose(storage,p->self);
  119.             }
  120.  
  121.         case kComponentCanDoSelect:
  122.             {
  123.                 ComponentCallCanDoParameters *p = (ComponentCallCanDoParameters *)params;
  124.                 return CanDoSelector(p->selector);
  125.             }
  126.         case kComponentVersionSelect: 
  127.             return GetVersion();
  128.     
  129.         case codecGetCodecInfo:
  130.             {
  131.                 CodecCallCDGetCodecInfoParameters *p = (CodecCallCDGetCodecInfoParameters *)params;
  132.                 return CDGetCodecInfo(storage,p->info);
  133.             }
  134.     
  135.         case codecPreDecompress :
  136.             {
  137.                 CodecCallCDPreDecompressParameters *p = (CodecCallCDPreDecompressParameters *)params;
  138.                 return CDPreDecompress(storage,p->params);
  139.             }
  140.             
  141.         case codecBandDecompress :
  142.             {
  143.                 CodecCallCDBandDecompressParameters *p = (CodecCallCDBandDecompressParameters *)params;
  144.                 return CDBandDecompress(storage,p->params);
  145.             }
  146.     
  147.         case codecCDSequenceFlush :
  148.             return CDCodecFlush(storage);
  149.     
  150.         case codecCDSequenceBusy :
  151.             return 0;
  152.     
  153.         case codecNewImageBufferMemory:
  154.             {
  155.                 CodecCallCDCodecNewImageBufferMemory *p = (CodecCallCDCodecNewImageBufferMemory *)params;
  156.                 return CDCodecNewImageBufferMemory(storage, p->params, p->flags, p->memoryGoneProc, p->refCon);
  157.             }
  158.         
  159.         default:
  160.             return badComponentSelector;
  161.     }    
  162. }
  163.  
  164. /************************************************************************************ 
  165.  *    This gets called when the component instance is opened. We allocate our storage at this
  166.  *    point. If we have shared globals, we check if they exist, and put a pointer to them 
  167.  *    in our instance globals so that other calls can get to them.
  168.  */
  169.  
  170. pascal ComponentResult
  171. CDOpen(ComponentInstance self)
  172. {
  173.     ComponentResult result = noErr;
  174.     Globals             *glob;
  175.     
  176.     /* 
  177.         First we allocate our local storage. This should store any
  178.         kind of data used by the thing instance. It should be allocated
  179.         in the current heap.
  180.     */     
  181.          
  182.     if ( (glob = (Globals *)NewPtrClear(sizeof(Globals))) == nil )  {
  183.         return MemError();
  184.     }
  185.     SetComponentInstanceStorage(self,(Handle)glob);
  186.  
  187. #ifdef POWERPC_NATIVE
  188.     glob->decompressCallBackUPP = &decompressCallBackUPP;
  189. #else
  190.     glob->decompressCallBackUPP = DecompressCallBack;
  191. #endif
  192.  
  193.     {
  194.         short i;
  195.         for (i = 0; i < (QUEUE_SIZE - 1); i++)
  196.             glob->queue[i].nextFree = &(glob->queue[i + 1]);
  197.         glob->firstFreeEntry = &(glob->queue[0]);
  198.     }
  199.  
  200. bail:
  201.     return result;
  202. }
  203.  
  204. /************************************************************************************ 
  205.  *    This gets called when the thing instance is closed. We need to get rid of any 
  206.  *    instance storage here. 
  207.  */
  208.  
  209. pascal ComponentResult
  210. CDClose(Handle storage,ComponentInstance self)
  211. {
  212.     Globals            *glob = (Globals *)storage;
  213.  
  214.     if (glob)
  215.         DisposePtr((Ptr)glob);
  216.  
  217.     return noErr;
  218. }
  219.  
  220.  
  221.  
  222. /************************************************************************************ 
  223.  *     Return true if we can handle the selector, otherwise false.
  224.  */
  225.  
  226. pascal ComponentResult
  227. CanDoSelector(short selector)
  228. {    
  229.     switch(selector) {
  230.         case kComponentOpenSelect:
  231.         case kComponentCloseSelect:
  232.         case kComponentCanDoSelect:
  233.         case kComponentVersionSelect: 
  234.         case codecGetCodecInfo:
  235.         case codecPreDecompress:
  236.         case codecBandDecompress:
  237.         case codecCDSequenceFlush:
  238.         case codecCDSequenceBusy:
  239.         case codecNewImageBufferMemory:
  240.             return true;
  241.         default:
  242.             return false;
  243.     }
  244. }
  245.  
  246.  
  247. /************************************************************************************ 
  248.  *    Return the version of this component ( defines interface ) and revision level
  249.  *    of the code.
  250.  */
  251.  
  252. pascal ComponentResult 
  253. GetVersion(void)
  254. {
  255.     return (codecInterfaceVersion<<16) | EXAMPLE_CODEC_REV;        /* interface version in hi word, code rev in lo word  */
  256. }
  257.  
  258. /*
  259.     CDCodecFlush is called when the image compression manager wants the 
  260.     codec to empty its schedule queue. An example would be when a movie is
  261.     playing and the user moves the thumb. The sudden jump in time renders
  262.     any previously scheduled frames useless. So we need to flush the queue so
  263.     we can start over.
  264. */
  265. pascal ComponentResult
  266. CDCodecFlush(Handle storage)
  267. {
  268.     Globals *glob = (Globals *)storage;
  269.  
  270.     /*
  271.         if there's not a callback proc allocated, we sure don't have any frames
  272.         queued up.
  273.     */
  274.     if (glob->callBack) {
  275.         DecompressRecord *drp;
  276.         long saveA5 = SetA5(glob->a5World);
  277.  
  278.         glob->queueIsRunning = false;    // flag queue as not running. 
  279.                                         // otherwise, we'll never start it up again
  280.  
  281.         // kill the callback
  282.         CancelCallBack(glob->callBack);
  283.  
  284.         // tear down the queue
  285.         while (drp = (DecompressRecord *)glob->head.qHead) {
  286.             drp = (DecompressRecord *)(((long)drp) - fieldOffset(DecompressRecord, next));
  287.             if (drp->inUse) {
  288.                 // call back to say we're done
  289.                 ICMDecompressComplete(glob->sequenceID, -1, codecCompletionSource | codecCompletionDest, &drp->completionProc);
  290.                 drp->nextFree = glob->firstFreeEntry;
  291.                 glob->firstFreeEntry = drp;
  292.                 drp->inUse = false;
  293.             }
  294.             Dequeue(glob->head.qHead, &glob->head);
  295.         }
  296.  
  297.         SetA5(saveA5);
  298.     }
  299.  
  300.     return noErr;
  301. }
  302.  
  303. /************************************************************************************ 
  304.  *    CDPreDecompress gets called before an image is decompressed. We return information about
  305.  *    how we can decompress the image to the codec manager, so that it can fit the destination data
  306.  *    to our requirements. 
  307.  */
  308.  
  309. pascal ComponentResult
  310. CDPreDecompress(Handle storage,register CodecDecompressParams *p)
  311. {
  312.     Globals    *glob  = (Globals *)storage;
  313.     register CodecCapabilities    *capabilities = p->capabilities;
  314.  
  315.     /* check to see if this base address is on our screen's GDevice */
  316.     {
  317.     GDHandle gd = LMGetMainDevice();
  318.     Rect bounds = (**gd).gdRect;
  319.     Ptr mainScreenBaseStart, mainScreenBaseEnd;
  320.  
  321.     mainScreenBaseStart = (**(**gd).gdPMap).baseAddr;
  322.     mainScreenBaseEnd = (**(**gd).gdPMap).baseAddr;
  323.     mainScreenBaseEnd += (bounds.bottom - bounds.top) * ((**(**gd).gdPMap).rowBytes & 0x3fff);
  324.  
  325.     if ((mainScreenBaseStart < p->dstPixMap.baseAddr) ||
  326.         (p->dstPixMap.baseAddr >= mainScreenBaseEnd)) {
  327.         return codecConditionErr;
  328.     }
  329.     }
  330.  
  331.     // only allow 16 bpp source
  332.     if ((**p->imageDescription).depth != 16)
  333.         return codecConditionErr;
  334.  
  335.     /* we only support 16 bits per pixel dest */
  336.     if (p->dstPixMap.pixelSize != 16)
  337.         return codecConditionErr;
  338.  
  339.     capabilities->wantedPixelSize = p->dstPixMap.pixelSize;    
  340.  
  341.     /*    The smallest possible band we can do is 1 scan lines. */
  342.     
  343.     capabilities->bandMin = 1;
  344.  
  345.     /*    We can deal with 1 scan line high bands. */
  346.  
  347.     capabilities->bandInc = 1;
  348.     
  349.     /*    If we needed our pixels to be aligned on some integer multiple we would set these to
  350.      *    the number of pixels we need the dest extended by. If we dont care, or we take care of
  351.      *  it ourselves we set them to zero.
  352.      */
  353.  
  354.     capabilities->extendWidth = 0;
  355.     capabilities->extendHeight = 0;
  356.  
  357.     capabilities->flags = codecCanAsyncWhen | codecCanAsync |
  358.                             codecCanScale | codecCanMask | codecCanRemapColor | codecCanFastDither;
  359.  
  360.  
  361.     capabilities->flags |= codecImageBufferIsOnScreen;
  362.  
  363.     glob->sequenceID = p->sequenceID;
  364.  
  365.     return noErr;
  366. }
  367.  
  368. /*
  369.     This is the call back proc that is used for scheduled asynchronously displayed
  370.     frames. It is called at the scheduled frame time, and should display that
  371.     frame. If there is a frame queued up after this one, it should be scheduled.
  372. */
  373. pascal void DecompressCallBack(QTCallBack cb,long refcon)
  374. {
  375.     DecompressRecord *drp = (DecompressRecord *)refcon;
  376.     Globals *glob = (Globals *)drp->glob;
  377.  
  378.     Dequeue((void *)(&drp->next), &glob->head);        // remove this frame from the queue
  379.  
  380.     if (drp->inUse) {
  381.         /* Be sure to call ICMDecompressComplete when done. */
  382.         ICMDecompressComplete(glob->sequenceID, noErr, codecCompletionSource | codecCompletionDest, &drp->completionProc);
  383.         drp->inUse = false;                // we're done with this frame
  384.  
  385.         drp->nextFree = glob->firstFreeEntry;
  386.         glob->firstFreeEntry = drp;
  387.  
  388.         // queue up the next one
  389.         if (glob->queueIsRunning && (drp = (void *)glob->head.qHead)) {
  390.             /* there is another frame scheduled. set up the callback task. */
  391.             drp = (DecompressRecord *)(((long)drp) - fieldOffset(DecompressRecord, next));
  392.             CallMeWhen(
  393.                     glob->callBack,
  394.                     glob->decompressCallBackUPP,
  395.                     (long)drp,
  396.                     (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
  397.                     (long)drp->frameTime,
  398.                     (long)drp->scale);
  399.         }
  400.         else {
  401.             // ran out of frames ... the queue is off, so mark it as such
  402.             glob->queueIsRunning = false;
  403.         }
  404.     }
  405. }
  406.  
  407. /************************************************************************************ 
  408.  *    CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal 
  409.  *    band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we 
  410.  *    demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and
  411.  *    we need to clear bits in it that correspond to any pixels in the destination we do not want to 
  412.  *    change. ( We always write all pixels, so we dont care. This mode is important only for those
  413.  *    codecs that have frame differencing and don't always write all the pixels. )
  414.  */
  415.  
  416. pascal ComponentResult
  417. CDBandDecompress(Handle storage,register CodecDecompressParams *p)
  418. {
  419.     OSErr                result = noErr;
  420.     Boolean                callCompletionRoutine = true;
  421.     Globals                *glob  = (Globals *)storage;
  422.  
  423.     if (p->frameTime) {
  424.         /* this frame has a schedule time to be displayed. */
  425.         DecompressRecord *drp;
  426.         long i;
  427.  
  428.         // find an empty slot in the queue
  429.         drp = glob->firstFreeEntry;
  430.         if (!drp) {        // we couldn't find an empty slot
  431.             result = codecCantQueueErr;
  432.             goto bail;
  433.         }
  434.  
  435.         if (!glob->callBack) {
  436.             // there is no current callback. allocate one.
  437.             glob->callBack = NewCallBack(p->frameTime->base,callBackAtTime + callBackAtInterrupt + callBackAtDeferredTask);
  438.             if (!glob->callBack) {
  439.                 result = codecCantQueueErr;
  440.                 goto bail;
  441.             }
  442.             glob->a5World = SetA5(0);
  443.             SetA5(glob->a5World);            // is there a way to get a5 without changing it?
  444.         }
  445.  
  446.         glob->firstFreeEntry = drp->nextFree;
  447.         
  448.         drp->frameTime = p->frameTime->value.lo;
  449.         drp->scale = p->frameTime->scale;
  450.         drp->rate = p->frameTime->rate;
  451.         drp->glob = (void *)glob;
  452.         drp->inUse = true;
  453.  
  454.         Enqueue((void *)(&drp->next), &glob->head);    // put the frame in the queue
  455.         if (!glob->queueIsRunning) {
  456.             // the queue isn't running... start it up
  457.             glob->queueIsRunning = true;    // since CallMeWhen could be considered "CallMeRightNow"
  458.             if (result = CallMeWhen(
  459.                     glob->callBack,
  460.                     glob->decompressCallBackUPP,
  461.                     (long)drp,
  462.                     (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
  463.                     (long)drp->frameTime,
  464.                     (long)drp->scale)) {
  465.                 // there was an error
  466.                 glob->queueIsRunning = false;    // if error, queue isn't running
  467.                 drp->inUse = false;
  468.                 Dequeue((void *)&drp->next, &glob->head);    // pull it out of the queue
  469.                 drp->nextFree = glob->firstFreeEntry;
  470.                 glob->firstFreeEntry = drp;
  471.                 goto bail;
  472.             }
  473.         }
  474.         callCompletionRoutine = false;
  475.     } 
  476.  
  477. bail:
  478.     if (callCompletionRoutine)
  479.         ICMDecompressComplete(p->sequenceID, result, codecCompletionSource | codecCompletionDest, &p->completionProcRecord);
  480.  
  481.     return result;
  482. }
  483.  
  484. /************************************************************************************ 
  485.  *    CDGetCodecInfo allows us to return information about ourselves to the codec manager.
  486.  *    
  487.  *    There will be a tool for determining appropriate values for the accuracy, speed
  488.  *    and level information. For now we estimate with scientific wild guessing.
  489.  *
  490.  *  The info is stored as a resource in the same file with our component.
  491.  */
  492.  
  493. pascal ComponentResult
  494. CDGetCodecInfo(Handle storage,CodecInfo *info)
  495. {
  496.     Globals *glob = (Globals *)storage;
  497.  
  498.     if ( info == nil ) 
  499.         return paramErr;
  500.  
  501.     BlockMoveData("\praw faker", info->typeName, 32);
  502.     info->version = 1;
  503.     info->revisionLevel = 1;
  504.     info->vendor = 'fake';
  505.     info->decompressFlags = codecInfoDoes32;
  506.     info->compressFlags = 0;
  507.     info->formatFlags = codecInfoDepth16 | codecInfoDepth32;
  508.     info->compressionAccuracy = 100;
  509.     info->decompressionAccuracy = 100;
  510.     info->compressionSpeed = 100;
  511.     info->decompressionSpeed = 98;
  512.     info->compressionLevel = 100;
  513.     info->resvd = 0;
  514.     info->minimumHeight = 1;
  515.     info->minimumWidth = 1;
  516.     info->decompressPipelineLatency = 0;
  517.     info->compressPipelineLatency = 0;
  518.     info->privateData = 0;
  519.  
  520.     return noErr;
  521. }
  522.  
  523. pascal ComponentResult
  524. CDCodecNewImageBufferMemory(Handle storage, CodecDecompressParams *p, long flags, ICMMemoryDisposedUPP memoryGoneProc, void *refCon)
  525. {
  526. #pragma unused(memoryGoneProc, refCon)
  527.  
  528.     OSErr err = noErr;
  529.     long offsetH, offsetV;
  530.     Ptr baseAddr;
  531.  
  532.     // call predecompress to check to make sure we can handle this destination
  533.     err = CDPreDecompress(storage, p);
  534.     if (err) goto bail;
  535.  
  536.     // calculate a base address to write to
  537.     offsetH = (p->dstRect.left - p->dstPixMap.bounds.left);
  538.     switch (p->dstPixMap.pixelSize) {
  539.         case 32:    offsetH *= 4;    break;
  540.         case 16:    offsetH *= 2;    break;
  541.         case  8:    offsetH *= 1;    break;
  542.         default:    return paramErr;
  543.     }
  544.  
  545.     offsetV = (p->dstRect.top - p->dstPixMap.bounds.top) * (0x7fff & p->dstPixMap.rowBytes);
  546.     baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
  547.  
  548.     // return the base address and row bytes to use in the dstPixMap structure
  549.     p->dstPixMap.baseAddr = baseAddr;
  550.     p->dstPixMap.rowBytes = p->dstPixMap.rowBytes;
  551.     p->capabilities->flags = codecImageBufferIsOnScreen;
  552.  
  553. bail:
  554.     return err;
  555. }
  556.  
  557. #ifdef LINK_EXAMPLE_CODEC
  558.  
  559. void InstallRawFakerCodec(void);
  560. void InstallRawFakerCodec(void)
  561.  
  562. {
  563.     ComponentDescription td;
  564.     Component    c;
  565.     Handle    dname;
  566.  
  567.     dname = NewHandle(19);
  568.     td.componentType = 'imdc';
  569.     td.componentSubType = 'raw ';
  570.     td.componentManufacturer = 'fake';
  571.     td.componentFlags = codecInfoDoes32;
  572.     td.componentFlagsMask = 0;
  573.  
  574.     BlockMoveData("\pTEST Raw Faker DECO",*dname,19);
  575.     if ((c= RegisterComponent(&td,NewComponentRoutineProc(RAWFAKEREXAMPLECODEC), 0,dname,nil, nil)) == 0 ) {
  576.         Debugger();
  577.     }
  578.     SetDefaultComponent(c,defaultComponentAnyManufacturer+defaultComponentAnyFlags);
  579. }
  580. #endif
  581.